package state

import (
	"osiris/dto"
	"osiris/logger"
	"osiris/mpt"
	"osiris/ocrypto/ecdsa"
	"sync"
)

const (
	InvalidNonce = -1
)

type StateTrie interface {

	// InitState 状态树初始化
	//  @return error
	InitState() error

	// ResetState 状态树重置，会删除状态树中存储的信息
	ResetState()

	// HasLeaf 判断状态树中是否存在key对应的叶子节点
	//  @param key
	//  @return bool
	HasLeaf(key []byte) bool

	// GetLeaf 从状态树里获得key对应的叶子节点
	//  @param key
	//  @return dto.StateLeaf
	//  @return bool
	GetLeaf(key []byte) (dto.StateLeaf, bool)

	// RootHash MPT根hash
	//  @return string
	RootHash() string

	// commitState 提交状态
	//  @param key
	//  @param leaf
	//  @param writeDB
	commitState(key []byte, leaf dto.StateLeaf, writeDB bool)

	// Lock 锁定状态树（不能长时间锁着）
	Lock()

	// Unlock 解锁状态树（必须和Lock成对出现）
	Unlock()
}

// BaseStateTrie 状态树基类
type BaseStateTrie struct {
	// stateTrie 状态树(注意：state包支持并发安全，再往下mpt包不支持并发全)
	stateTrie *mpt.Trie

	// stateMutex 状态树的锁
	stateMutex *sync.RWMutex

	//TODO 多通道/多链
	// ChainID 属于哪一条链
	ChainID int
}

// HasLeaf 判断状态树中是否存在key对应的叶子节点
//
//	@receiver trie
//	@param key
//	@return bool
func (trie *BaseStateTrie) HasLeaf(key []byte) bool {
	trie.stateMutex.RLock()
	_, exist := trie.stateTrie.Get(key)
	trie.stateMutex.RUnlock()
	return exist
}

// GetLeaf 从状态树里获得key对应的叶子节点
//
//	@receiver trie
//	@param key
//	@return dto.StateLeaf 叶子节点
//	@return bool 叶子节点是否存在
func (trie *BaseStateTrie) GetLeaf(key []byte) (dto.StateLeaf, bool) {
	var leaf dto.StateLeaf
	trie.stateMutex.RLock()
	valueBytes, exist := trie.stateTrie.Get(key)
	trie.stateMutex.RUnlock()
	if !exist {
		return leaf, false
	}

	err := dto.FromBytes(valueBytes, &leaf)
	if err != nil {
		logger.Error(map[string]interface{}{"[state] [Get Leaf] decode StateLeaf from bytes": err})
		return leaf, false
	}

	return leaf, true
}

func (trie *BaseStateTrie) RootHash() string {
	hashBytes := trie.stateTrie.Hash()
	return ecdsa.Keccak256HashBytesToStr(hashBytes)
}

func (trie *BaseStateTrie) Lock() {
	trie.stateMutex.Lock()
}

func (trie *BaseStateTrie) UnLock() {
	trie.stateMutex.Unlock()
}

func (trie *BaseStateTrie) RLock() {
	trie.stateMutex.RLock()
}

func (trie *BaseStateTrie) RUnLock() {
	trie.stateMutex.RUnlock()
}
